#!/bin/bash

IFS=$'\n'

# Get device path
get_dev_path() {
	echo /sys/bus/$1/devices/$2
}

# Get DPAA2 ethernet device path
get_dpaa2_dev_path() {
	get_dev_path "fsl-mc" $1
}

# Probe driver of device
dev_drv_probe() {
	local bus
	local dev
	bus=${1%%/devices/*}
	dev=${1##*/devices/}
	echo $dev > $bus/drivers_probe
}

# Bind device to driver
bind_dev_to_drv() {
	local dev
	local drv
	dev=${1##*/devices/}
	drv=$(readlink -e $1/driver)
	if [ "${drv##*/drivers/}" != "$2" ]; then
		echo $dev > $drv/unbind
	fi
	echo $2 > $1/driver_override
}

# Unbind device from driver
unbind_dev_from_drv() {
	local dev
	local drv
	dev=${1##*/devices/}
	drv=$(readlink -e $1/driver)
	if [ "$drv" != "" ]; then
		echo $dev > $drv/unbind
	fi
	echo "" > $1/driver_override
}

# Get OF path from device path
get_of_path() {
	echo $1/of_node
}

# Get device path from phandle
get_dev_path_by_phandle() {
	local d
	for d in $(find $1 -maxdepth 1 -type l); do
		if cmp -s $(get_of_path $d)/phandle $2 -n 4 -i 0:$3; then
			echo $d
			break
		fi
	done
}

# Get soc type
get_soc_type() {
	local s
	local -a strs
	readarray -d '' strs < /sys/firmware/devicetree/base/compatible
	for s in ${strs[*]}; do
		case $s in
		fsl,*-*)
			;;
		fsl,*)
			echo ${s#fsl,}
			break
			;;
		esac
	done
}

# Get number of cores from soc type
get_soc_cores_nr() {
	case $1 in
	l??04*)
		echo 4
		;;
	l??08*)
		echo 8
		;;
	l??16*)
		echo 16
		;;
	*)
		echo 1
		;;
	esac
}

# Connection of DPAA2 object
dpobj_connect() {
	restool -s dprc connect $1 --endpoint1=$2 --endpoint2=$3
}

# Disconnection of DPAA2 object
dpobj_disconnect() {
	restool -s dprc disconnect $1 --endpoint=$2 2>/dev/null
}

# Destruction of DPAA2 object
dpobj_destroy() {
	restool -s ${1%.*} destroy $1 1>/dev/null
}

# Assignment of DPAA2 object
dpobj_assign() {
	restool -s dprc assign $1 --object=$2 --child=$3 --plugged=$4
}

# Get endpoint of DPAA2 object
dpobj_get_endpoint() {
	restool -s ${1%.*} info $1 | sed -ne 's|^endpoint:\s\+\(\S\+\),.*|\1|p'
}

# Enumerate DPAA2 objects of DPRC
dpobj_enum() {
	restool -s dprc show $1 | sed -ne 's|^\(\S\+\)\s\+plugged|\1|p'
}

# Creation of DPRC
dprc_create() {
	restool -s dprc create $1 --label="$2" --options=$3
}

# Synchronization of DPRC
dprc_sync() {
	restool -s dprc sync
}

# Creation of DPMAC
dpmac_create() {
	restool -s dpmac create --mac-id=$1 --container=$2
}

# Creation of DPMCP
dpmcp_create() {
	restool -s dpmcp create --container=$1
}

# Creation of DPBP
dpbp_create() {
	restool -s dpbp create --container=$1
}

# Creation of DPCON
dpcon_create() {
	restool -s dpcon create --num-priorities=$1 --container=$2
}

# Creation of DPIO
dpio_create() {
	restool -s dpio create --channel-mode=$1 --num-priorities=$2 --container=$3
}

# Creation of DPNI
dpni_create() {
	restool -s dpni create --options=$1 --num-tcs=$2 --num-queues=$3 --qos-entries=$4 --num-cgs=$5 --container=$6
}

# Set MAC address of DPNI
dpni_set_mac_addr() {
	restool -s dpni update $1 --mac-addr=$2
}

# Get MAC address of DPNI
dpni_get_mac_addr() {
	restool -s dpni info $1 2>/dev/null | sed -ne 's|^mac address:\s\+\(\S\+\)|\1|p'
}

# Get phy connection type of DPMAC
get_phy_connection_type() {
	local str
	local ether_name
	ether_name=ethernet@$(printf "%x" ${2#dpmac.})
	read -d '' str 2>/dev/null < $(readlink -e $(get_of_path $1)/dpmacs/$ether_name/phy-connection-type)
	echo $str
}

# Get PHY device name of DPMAC
get_phy_name() {
	local ether_name
	local phy_dev_path
	local phy_node
	ether_name=ethernet@$(printf "%x" ${2#dpmac.})
	phy_dev_path=$(get_dev_path_by_phandle $(get_dev_path "mdio_bus") $(get_of_path $1)/dpmacs/$ether_name/phy-handle 0)
	phy_node=$(readlink -e $(get_of_path $phy_dev_path))
	echo ${phy_node##*/}
}

# Print help
print_help() {
	echo "Tool for bind DPAA2 ethernet devices"
	echo "Usage:"
	echo "Bind ethernet devices"
	echo "$0 dpni dpni.x dpmac.x dpsw.x.y dpdmux.x.y"
	echo
	echo "Config number of dpmcp/dpbp/dpcon/dpio for bind ethernet devices"
	echo "$0 -p X:X:X:X dpni dpni.x dpmac.x dpsw.x.y dpdmux.x.y"
	echo
	echo "Unbind ethernet devices in specified dprc"
	echo "$0 -u -c dprc.x"
	echo
	echo "Show ethernet devices already bind in specified dprc"
	echo "$0 -c dprc.x"
	echo
	echo "Acceptable arguments are dpni, dpni.x and dpmac.x"
	echo -e "\tdpni       = This specify that 1 DPNI object will be create"
	echo -e "\tdpni.x     = This specify that 1 DPNI object will be create and connect to dpni.x"
	echo -e "\tdpmac.x    = This specify that 1 DPNI object will be create and connect to dpmac.x"
	echo -e "\tdpsw.x.y   = This specify that 1 DPNI object will be create and connect to dpsw.x.y"
	echo -e "\tdpdmux.x.y = This specify that 1 DPNI object will be create and connect to dpdmux.x.y"
}

# Echo in red
echo_red() {
	echo -e '\033[0;31m'$*'\033[0m'
}

# Echo in green
echo_green() {
	echo -e '\033[0;32m'$*'\033[0m'
}

unset soc
unset cores
unset fsl_mc_path
unset dprc
unset root_dprc
unset parent_dprc
unset dprc_drv
unset dpio_drv
unset dpni_drv
unset dpmac_drv
unset unbind
unset dpmcp_nr
unset dpbp_nr
unset dpcon_nr
unset dpio_nr
unset dpmac_list
unset dpmcp_list
unset dpbp_list
unset dpcon_list
unset dpio_list
unset dpni_list
unset dpni_ad_list
unset dpni_ep_list

root_dprc="dprc.1"
parent_dprc=$root_dprc
dprc_drv="fsl_mc_dprc"
dpio_drv="sb_dpaa2-dpio"
dpni_drv="sb_dpaa2-dpni"
dpmac_drv="sb_dpaa2-dpmac"

declare -a dpmac_list
declare -a dpmcp_list
declare -a dpbp_list
declare -a dpcon_list
declare -a dpio_list
declare -a dpni_list
declare -a dpni_ad_list
declare -a dpni_ep_list

soc=$(get_soc_type)
cores=$(get_soc_cores_nr $soc)
fsl_mc_path=$(get_dev_path "platform" "*.fsl-mc")

while [ $# -ne 0 ]; do
	case $1 in
	-h)
		print_help
		exit
		;;
	-p)
		shift
		dpmcp_nr=$(cut -f 1 -d ':' <<< $1)
		dpbp_nr=$(cut -f 2 -d ':' <<< $1)
		dpcon_nr=$(cut -f 3 -d ':' <<< $1)
		dpio_nr=$(cut -f 4 -d ':' <<< $1)
		dpmcp_nr=${dpmcp_nr:-16}
		dpbp_nr=${dpbp_nr:-24}
		dpcon_nr=${dpcon_nr:-8}
		dpio_nr=${dpio_nr:-$cores}
		shift
		;;
	-c)
		shift
		parent_dprc=$1
		shift
		;;
	-u)
		unbind="yes"
		shift
		;;
	*)
		break
		;;
	esac
done

if [ "${unbind-no}" == "yes" ]; then
	dprc=$parent_dprc
	if [ "$dprc" == "$root_dprc" ]; then
		echo_red "\n\tCannot unbind devices in \"$dprc\"\n\n"
		exit 1
	fi

	for obj in $(dpobj_enum $dprc); do
		obj_path=$(get_dpaa2_dev_path $obj)
		unbind_dev_from_drv $obj_path
		echo_green "Unbind \"$obj\" from driver"
		dpobj_destroy $obj
		echo_green "Destroy \"$obj\""
	done
	dprc_path=$(get_dpaa2_dev_path $dprc)
	unbind_dev_from_drv $dprc_path
	echo_green "Unbind \"$dprc\" from driver"
	dpobj_destroy $dprc
	echo_green "Destroy \"$dprc\""
	dprc_sync
	exit
elif [ $# -gt 0 ]; then
	for obj in $*; do
		type=${obj%%.*}
		case $type in
		dpmac|dpni|dpdmux|dpsw)
			;;
		*)
			echo_red "\n\tInvalid Argument \"$obj\"\n\n"
			exit 1
			;;
		esac
	done

	opts="DPRC_CFG_OPT_SPAWN_ALLOWED,DPRC_CFG_OPT_ALLOC_ALLOWED,DPRC_CFG_OPT_OBJ_CREATE_ALLOWED"
	opts=$opts",DPRC_CFG_OPT_TOPOLOGY_CHANGES_ALLOWED,DPRC_CFG_OPT_IRQ_CFG_ALLOWED"

	dprc=$(dprc_create $parent_dprc "Simplebits" $opts)
	echo_green "\"$dprc\" created in \"$parent_dprc\""

	dprc_sync

	dprc_path=$(get_dpaa2_dev_path $dprc)
	bind_dev_to_drv $dprc_path $dprc_drv
	dev_drv_probe $dprc_path
	echo_green "Bind \"$dprc\" to \"$dprc_drv\" driver"

	for idx in $(seq 1 $dpmcp_nr); do
		obj=$(dpmcp_create $dprc)
		dpmcp_list[${#dpmcp_list[*]}]=$obj
		echo_green "Create \"$obj\""
	done

	for idx in $(seq 1 $dpbp_nr); do
		obj=$(dpbp_create $dprc)
		dpbp_list[${#dpbp_list[*]}]=$obj
		echo_green "Create \"$obj\""
	done

	for idx in $(seq 1 $dpcon_nr); do
		obj=$(dpcon_create 2 $dprc)
		dpcon_list[${#dpcon_list[*]}]=$obj
		echo_green "Create \"$obj\""
	done

	opts="DPIO_LOCAL_CHANNEL"
	for idx in $(seq 1 $dpio_nr); do
		obj=$(dpio_create $opts 2 $dprc)
		dpio_list[${#dpio_list[*]}]=$obj
		echo_green "Create \"$obj\""
	done

	opts="DPNI_OPT_NO_FS"
	[ "$soc" == "ls1088a" ] || opts=$opts",0x80000000"
	for obj in $*; do
		type=${obj%%.*}
		idx=${#dpni_list[*]}
		dpni=$(dpni_create $opts 1 $cores 1 1 $dprc)
		echo_green "Create \"$dpni\""
		if [ "$type" == "dpmac" ]; then
			dpmac_path=$(get_dpaa2_dev_path $obj)
			if [ -e $dpmac_path ]; then
				unbind_dev_from_drv $dpmac_path
				echo_green "Unbind \"$obj\" from driver"
				dpobj_disconnect $root_dprc $obj
				echo_green "Disconnect \"$obj\" from connection"
				dpobj_destroy $obj
				dprc_sync
				echo_green "Destroy \"$obj\""
			fi
			obj=$(dpmac_create ${obj#dpmac.} $dprc)
			dpmac_list[${#dpmac_list[*]}]=$obj
			echo_green "Create \"$obj\""
			mac_addr=$(printf "00:00:00:00:00:%02x" ${obj#dpmac.})
		else
			mac_addr=$(printf "00:00:05:00:00:%02x" ${dpni#dpni.})
		fi
		dpni_set_mac_addr $dpni $mac_addr
		dpni_list[$idx]=$dpni
		dpni_ad_list[$idx]=$mac_addr
		if [ "$obj" == "dpni" ]; then
			dpni_ep_list[$idx]=""
		else
			dpobj_connect $root_dprc $dpni $obj
			dpni_ep_list[$idx]=$obj
			echo_green "Connect \"$dpni\" with \"$obj\""
		fi
	done

	for obj in ${dpmcp_list[*]} ${dpbp_list[*]} ${dpcon_list[*]}; do
		dpobj_assign $dprc $obj $dprc 1
	done

	dprc_sync

	for obj in ${dpio_list[*]}; do
		obj_path=$(get_dpaa2_dev_path $obj)
		bind_dev_to_drv $obj_path $dpio_drv
		dev_drv_probe $obj_path
		echo_green "Bind \"$obj\" to \"$dpio_drv\" driver"
	done

	for obj in ${dpmac_list[*]}; do
		obj_path=$(get_dpaa2_dev_path $obj)
		bind_dev_to_drv $obj_path $dpmac_drv
		dev_drv_probe $obj_path
		echo_green "Bind \"$obj\" to \"$dpmac_drv\" driver"
	done

	for obj in ${dpni_list[*]}; do
		obj_path=$(get_dpaa2_dev_path $obj)
		bind_dev_to_drv $obj_path $dpni_drv
		dev_drv_probe $obj_path
		echo_green "Bind \"$obj\" to \"$dpni_drv\" driver"
	done

	for obj in ${dpio_list[*]} ${dpmac_list[*]} ${dpni_list[*]}; do
		dpobj_assign $dprc $obj $dprc 1
	done
else
	dprc=$parent_dprc
fi

unset dpni_list
declare -a dpni_list

for obj in $(dpobj_enum $dprc); do
	type=${obj%%.*}
	if [ "$type" == "dpni" ]; then
		dpni_list[${#dpni_list[*]}]=$obj
	fi
done

echo -e "\n\n######################### Configured Interfaces #########################\n\n"
printf "%-21s %-21s %-21s %-21s %-21s %-25s\n" "Interface Name" "Endpoint Name" "Connection Type" "Phy Device Name" "Mac Address"
printf "%-21s %-21s %-21s %-21s %-21s %-25s\n" "==============" "=============" "===============" "===============" "=================="

for idx in $(seq 0 $((${#dpni_list[*]}-1))); do
	dpni=${dpni_list[$idx]}
	ep=$(dpobj_get_endpoint $dpni)
	mac_addr=$(dpni_get_mac_addr $dpni)
	if [ "${ep%.*}" == "dpmac" ]; then
		conn_type=$(get_phy_connection_type $fsl_mc_path $ep)
		phy_name=$(get_phy_name $fsl_mc_path $ep)
	else
		conn_type=""
		phy_name=""
	fi
	[ "$ep" != "" ] || ep="<No Endpoint>"
	[ "$conn_type" != "" ] || conn_type="<Unknown>"
	[ "$phy_name" != "" ] || phy_name="<Unknown>"
	[ "$mac_addr" != "" ] || mac_addr="<Unknown>"
	printf "%-21s %-21s %-21s %-21s %-25s\n" ${dpni/./-} $ep $conn_type $phy_name $mac_addr
done
